$$\Huge Ο = πx.(πy.x \space (y \space y)) \space (πy.x \space (y \space y))$$
Calchylus
is a combination of Hy macro and Lambda term dictionary. It was written to help to write and evaluate Lambda expressions.
[Lambda calculus](https://en.wikipedia.org/wiki/Lambda_calculus) is a formal system in mathematical logic for expressing computation based on function abstraction and application using variable binding and substitution. - wikipedia.org
The short π
macro presented below, makes it possible to define and evaluate Lambda expressions the most conventional way. There is really not much to implement because Hy is already a LISP language. LISP, on the other hand, can be defined as an untyped Lambda calculus extended with constants. So actually we just need to introduce π
macro, simplify the usual LISP notation, and act only with functions.
At the current development stage, Calchylus
does not provide alpha and beta reduction stages of the terms, just direct evaluation.
Calchylus
Jupyter notebook document and GitHub repository was initialized by Marko Manninen, 08/2017.
π
) macro
InΒ [1]:
; index-find function needs to be available at compile time for the lambda expression macro
(eval-and-compile
; set comma constant for separator
; at the moment Hy doesn't support dot on macro expressions because dot is mixed with internal HyCons functionality
; causing this error: https://github.com/hylang/hy/blob/e8ffd412028232cc2cc4fe4bfb10f21ce8ab2565/hy/compiler.py#L2478
(setv comma 'Β·)
; find index of the element from the list. if the element is not found, return -1
(defn index-find [elm lst]
(try (.index lst elm) (except [ValueError] -1))))
; lambda expression macro
(defmacro π [&rest expr]
(setv idx (index-find comma expr))
`(fn ~(cut expr 0 (if (pos? idx) idx 0)) ~@(cut expr (inc idx))))
Out[1]:
Parentheses (
)
to elaborate function scope, group terms, prevent disambiguity, and wrap application. Lambda symbol π
to mark the beginning of the function. Dot separator Β·
to distinct function arguments and function body from each other.
Lambda variables. Any symbol following Hy definitions are accepted. For example single character variable names like x
and y
, multi character names args
, also with capitals FUNC
, are allowed.
Lambda abstraction. For example (π x Β· x)
creates a definition of the function (fn [x] x)
, or $x \mapsto
x$ in mathematic notation.
((π x Β· x) 1)
calls for anonymous function with input value 1
.
InΒ [2]:
(macroexpand '(π x Β· x))
Out[2]:
Lambda application is the procedure to evaluate declared functions with a given input. Applications are similar to this:
$$\Large((π \space x Β· x)\space y) \implies y$$where the first x
is a name of the argument of the function. The second x
is the return value of the function. y
is the input value passed to the function. Later in the document we will use $\implies$ character to denote, if lambda term reduces to simpler terms or if application evaluates to a certain value.
Let us next utilize above application and pass an argument to so called identity function.
InΒ [3]:
((π x Β· x) 1) ; ((π x Β· x) 1) β‘ 1
Out[3]:
Output is as expected, not very exciting one.
Argument passed to the function can also be yet another function. Say, we want to pass the identity function to the identity function instead of the static value, then the appication would look this:
$$\Large((π \space x Β· x) \space (π \space x Β· x)) \implies (π \space x Β· x)$$The outcome of such application is a function, thus it alone cannot evaluate to any value. Rather we need to treat it as a function part of the application and provide input for it:
InΒ [4]:
(((π x Β· x) (π x Β· x)) 1)
Out[4]:
Again output is expected to be 1
because first we pass identity function a
to the identity function b
and then we pass the value 1
to the identity function b
.
One more step toward deeper nested structures is to call function inside a function:
$$\Large((π \space x \space Β· ((π \space x \space Β· x) \space x)) \space 1) \implies 1$$
InΒ [5]:
((π x Β· ((π x Β· x) x)) 1)
Out[5]:
We can see how the result is same in all three examples.
Note also how variable names are same on nested functions. This works because of safe scoping in Hy, but one might understand functionality more clear if different variable names were used like this:
$$\Large(π \space x \space Β· ((π \space y \space Β· y) \space x))$$Restriction of the traditional Lambda calculus is that it allows one and only one argument per function. Programmers on most of the modern languages are used to multiary functions. There is a way to imitate multiple argument passing with a nested structure called currying in Lambda calculus.
$$\Large(π \space x \space Β· (π \space y \space Β· (π \space z \space Β· x \space y \space z)))$$Let us make a simple multiplication with two variables for instance:
InΒ [6]:
((((π x Β· (π y Β· (π z Β· (* x y z)))) 2) 2) 2)
Out[6]:
InΒ [7]:
((π x y z Β· (* x y z)) 2 2 2)
Out[7]:
InΒ [8]:
((π x Β· 2) 1)
Out[8]:
InΒ [9]:
((π Β· 2))
Out[9]:
InΒ [10]:
(((π func Β· (π arg Β· (func arg))) (π x Β· x)) 2)
Out[10]:
InΒ [11]:
(, ((π x Β· x) 1) ((π x Β· x) 2) ((π x Β· x) 3))
Out[11]:
Select first:
InΒ [12]:
((((π x Β· (π y Β· (π f Β· ((f x) y)))) 1) 2) (π x Β· (π y Β· x)))
Out[12]:
Select second:
InΒ [13]:
((((π x Β· (π y Β· (π f Β· ((f x) y)))) 1) 2) (π x Β· (π y Β· y)))
Out[13]:
InΒ [14]:
; ask for the second but because arguments are swapped, the first one is returned
((((π x Β· (π y Β· (π f Β· ((f y) x)))) 1) 2) (π x Β· (π y Β· y)))
Out[14]:
InΒ [15]:
(try
; we will catch recursion error from this line, otherwise output would be messier
((π x Β· (x x)) (π x Β· (x x)))
(except (e RecursionError)
(print "Eternal loop achieved!")))
InΒ [16]:
(π x Β· (+ x y))
Out[16]:
But unbound variable y will become a problem on evaluation of the application:
InΒ [17]:
(try
((π x Β· (+ x y)) 1)
(except (e NameError)
(print e)))
http://www.ics.uci.edu/~lopes/teaching/inf212W12/readings/lambda-calculus-handout.pdf
InΒ [18]:
; select the first
(((π x Β· (π y Β· x)) 1) 2)
Out[18]:
InΒ [19]:
; select the second
(((π x Β· (π y Β· y)) 1) 2)
Out[19]:
IF-THEN-ELSE condition can be formed with boolean true / false selectors. As we defined earlier, true selector returns the first given argument, and false selector returns the second argument. Thus in Lambda calculus we can produce IF-THEN-ELSE functionality by passing either true or false selector to the function and let it select between the first options and the second option.
Let proposition P
be the boolean selector, a
the first option, and b
the second option. Formal definition of the IF-THEN-ELSE Lambda term would be:
In the next example P
is $(π \space x Β· (π \space y Β· x))$ (i.e. true), a
is literal T, and b
is literal F:
InΒ [20]:
((((π P Β· (π a Β· (π b Β· ((P a) b)))) (π x Β· (π y Β· x))) 'T) 'F)
Out[20]:
Or with a shorter notation, this time P
is $(π \space x Β· (π \space y Β· y))$ (i.e. false):
InΒ [21]:
((π P a b Β· (P a b)) (π x y Β· y) 'T 'F)
Out[21]:
InΒ [22]:
((((π x Β· ((x (π x Β· (π y Β· y))) (π x Β· (π y Β· x)))) (π x Β· (π y Β· x))) 'T) 'F)
Out[22]:
InΒ [23]:
(((π x Β· (x (π x y Β· y) (π x y Β· x))) (π x y Β· y)) 'T 'F)
Out[23]:
InΒ [24]:
; IF a is TRUE AND b is TRUE, THEN x, ELSE y
; a is TRUE, b is TRUE, x is T, y is F -> T
(((((π a Β· (π b Β· ((a b) (π x Β· (π y Β· y))))) (π x Β· (π y Β· x))) (π x Β· (π y Β· x))) 'T) 'F)
Out[24]:
InΒ [25]:
(((π a b Β· (a b (π x y Β· y))) (π x y Β· x) (π x y Β· y)) 'T 'F)
Out[25]:
InΒ [26]:
; IF a is TRUE OR b is TRUE, THEN x, ELSE y
; a is TRUE, b is FALSE, x is T, y is F -> T
(((((π a Β· (π b Β· ((a (π x Β· (π y Β· x))) b))) (π x Β· (π y Β· x))) (π x Β· (π y Β· y))) 'T) 'F)
Out[26]:
InΒ [27]:
(((π a b Β· (a (π x y Β· x) b)) (π x y Β· y) (π x y Β· y)) 'T 'F)
Out[27]:
InΒ [28]:
; loop until given limit and sum up all values -> (summa 10)
; x = function, y = steps, z = result
((π x y Β· (x x y 0)) (π x y z Β· (if (> y 0) (x x (- y 1) (+ y z)) z)) 10)
Out[28]:
InΒ [29]:
; loop until given limit and sum up all values -> (product 7)
; x = function, y = steps, z = result
((π x y Β· (x x y 1)) (π x y z Β· (if (> y 0) (x x (- y 1) (* y z)) z)) 7)
Out[29]:
InΒ [30]:
;Ξ»t.(Ξ»x.t (x x)) (Ξ»x.t (x x))
;(((π x Β· ((π y Β· (x (y y))) (π y Β· (x (y y))))) (π f Β· (π n Β· (if (= n 1) 1 (* n (f (dec n))))))) 7)
;(((fn [f] ((fn [x] (x x)) (fn [x] (f (fn [y] ((x x) y)))))) (fn [f] (fn [n] (if (< n 3) n (* n (f (dec n))))))) 5)
(((π f Β· ((π x Β· (x x)) (π x Β· (f (π y Β· ((x x) y)))))) (π f Β· (π n Β· (if (< n 3) n (* n (f (dec n))))))) 5)
Out[30]:
Copyright (c) 2017 Marko Manninen